/**
 * 节点预加载，创建,销毁，管理类
 */

import MoudleName from "../MoudleName";
import LoadingView from "./LoadingView";
import ToastView from "./ToastView";

type AssetName = "coin" | "loading" | "wait" | "game_font" | "LoadingView" | "ToastView" |
    "coin" | "wait" | "click" | "game_bgm" | "goldAnim" | "goldAnim" |
    "singleColor" | "toast_frame" | "wait_round" | "initPrefab" | "initPrefab123";

export default class UIManager {
    private static _viewStack: Map<string, cc.Node> = new Map();//局部视图缓存栈
    private static _loadMaxNum: number = 3; //网络不好情况下，页面最多重新加载几次
    private static _loadcurrentNum: number = 0;//网络不好情况下，当前页面重新次数
    private static _loadTime: number = 3; //网络不好情况下，间隔几秒加载

    /**
     * 加载所有预制体
     * @param assetsUrl 路径名称（缓存名称）！！！注意两个要一样
     * @param callback 加载进度回调函数
     * @param callback.progress 加载进度
     * @param callback.isCompleted 是否加载完成
     */
    public static loadAssets(assetsUrl: string, callback?: (progress: number, isCompleted: boolean) => void) {
        if (MoudleName[assetsUrl].size != 0) {
            console.log(`${assetsUrl}资源已加载，无需加载`);
            callback && callback(100, true);
            return
        }

        cc.resources.loadDir(assetsUrl, cc.Asset, (completedCount, totalCount, item) => {
            // // 计算进度
            let progress = Math.floor((completedCount / totalCount) * 100);
            callback && callback(progress, false);
        }, (error, assets) => {
            if (error) {
                cc.error(error)
                return
            }

            for (let i = 0; i < assets.length; i++) {
                MoudleName[assetsUrl].set(assets[i].name, assets[i]);
            }
            callback && callback(100, true);

            console.log(`${assetsUrl}加载完成，准备起飞\u2708\ufe0f `, MoudleName[assetsUrl]);
        })
    }

    /**
     * 加载一个预制体
     * @param storageName 路径名称（缓存名称）！！！注意两个是一样的
     * @param name 页面视图名称
     * @param isLoading 是否展示加载页面
     */
    public static async loadPrefab(storageName: string, name: string, isLoading: boolean = false): Promise<cc.Node> {
        if (isLoading) LoadingView.showLoading();
        return new Promise((resolve, reject) => {
            if (MoudleName[storageName].has(name) && MoudleName[storageName].get(name).isValid) {
                let node: cc.Node = cc.instantiate(MoudleName[storageName].get(name));
                if (isLoading) LoadingView.hideLoading();
                resolve(node);
            } else {
                cc.resources.load(`${storageName}/prefab/${name}`, cc.Prefab, (err, prefab: cc.Prefab) => {
                    if (err) {
                        if (this._loadcurrentNum < this._loadMaxNum) {
                            console.error(`${storageName}/${name}，第${this._loadcurrentNum}次加载失败，${JSON.stringify(err)}`);

                            setTimeout(() => {
                                ToastView.showToastView(`第${this._loadcurrentNum}次加载失败`);
                                UIManager.loadPrefab(storageName, name, false);
                            }, this._loadTime * 1000);

                        } else {
                            setTimeout(() => {
                                ToastView.showToastView(`请检查当前网络`);
                                LoadingView.hideLoading();
                            }, this._loadTime * 1000);
                        }
                        reject(err);
                        this._loadcurrentNum += 1;
                        return;
                    }
                    let node = cc.instantiate(prefab);
                    MoudleName[storageName].set(name, prefab);
                    this._loadcurrentNum = 0;
                    if (isLoading) LoadingView.hideLoading();
                    resolve(node);
                });
            }
        })
    }


    /**
     * 创建节点
     * @param storageName 缓存的名字，对应路径
     * @param nodeName 视图的名字
     * @param optional. 参数
     * @param optional.nodeParent 挂载到哪个节点上，默认Canvas
     * @param optional.position 挂载的位置，默认cc.Vec3(0, 0, 0)
     * @param optional.showAnimation 是否开启打开动画，默认为true
     * @param optional.isLoading 是否在打开页面之前有loading动画，默认为true
     */
    public static async createNode(storageName: string, nodeName: AssetName, optional: {
        nodeParent?: cc.Node,
        position?: cc.Vec3,
        setData?: any,
        showAnimation?: boolean,
        isLoading?: boolean
    } = {}): Promise<cc.Node> {
        //参数初始化
        let { nodeParent, position, setData, showAnimation, isLoading } = optional;
        nodeParent = optional.nodeParent || cc.director.getScene();
        position = optional.position || new cc.Vec3(0, 0, 0);
        // setData = optional.setData || null;
        showAnimation = optional.showAnimation !== undefined ? optional.showAnimation : true;
        isLoading = optional.isLoading !== undefined ? optional.isLoading : false;
        //判断缓存中是否有节点，没有就重新加载
        let node = null;
        if (this._viewStack.has(nodeName)) {
            node = this._viewStack.get(nodeName);
        } else if (MoudleName[storageName].has(nodeName) && MoudleName[storageName].get(nodeName).isValid) {
            node = cc.instantiate(MoudleName[storageName].get(nodeName));
        } else {
            console.warn(`局部缓存中没有可用 ${nodeName} 资源`, MoudleName[storageName]);
            node = await this.loadPrefab(storageName, nodeName, isLoading);
        }
        //打开动画
        if (showAnimation) {
            let targetNode = cc.find('all', node);
            if (targetNode && targetNode.isValid) {
                targetNode.opacity = 0;
                targetNode.scale = 0;
                // targetNode.setScale(0);
                cc.tween(targetNode).to(.25, { opacity: 255, scale: 1 }, { easing: "backOut" }).start();
            }
        }
        // 添加关闭事件，和关闭动画
        let closeNode = cc.find("all/S_close", node);
        if (closeNode && closeNode.isValid && !closeNode.getComponent(cc.Button)) {
            let button = closeNode.addComponent(cc.Button);
            button.transition = cc.Button.Transition.SCALE;
            let event = new cc.Component.EventHandler();
            event.target = node;
            event.component = node.name;
            event.handler = "clickClose";
            if (!node.getComponent(node.name).clickClose) {
                node.getComponent(node.name).clickClose = () => {
                    // let targetNode = cc.find('all', this.node);
                    // cc.tween(targetNode).to(.25, { opacity: 0, scale: 0 }, { easing: "backIn" }).call(() => {
                    //     this.node.destroy();
                    // }).start();
                    this.deleteNode(nodeName);
                };
            }
            button.clickEvents.push(event);
        }

        //给节点传值
        if (typeof setData !== "undefined") {
            let script = node.getComponent(node.name);
            if (script && script["setData"]) {
                script["setData"](setData);
            } else {
                console.warn(`没有setData函数`, node.name);
            }
        }

        node.position = position;
        node.parent = nodeParent;
        node.getComponent(node.name).onShow();
        this._viewStack.set(nodeName, node);
        return node;
    }

    /**
     * 销毁页面
     * @param nodeName 想要删除页面的名字
     * @param optional 可选参数
     * @param optional.showAnimation 关闭动画
     * @param optional.isDestroy 是否直接删除，复用不高的话可选
     */
    public static deleteNode(nodeName: AssetName,
        optional: {
            showAnimation?: boolean,
            isDestroy?: boolean
        } = {}): void {
        let { showAnimation, isDestroy } = optional;
        showAnimation = optional.showAnimation || true;
        isDestroy = optional.isDestroy || false;

        if (this._viewStack.has(nodeName)) {
            let node = this._viewStack.get(nodeName);
            let targetNode = cc.find('all', node);
            if (targetNode && showAnimation) {
                cc.tween(targetNode).to(.25, { opacity: 0, scale: 0 }, { easing: "backIn" }).call(() => {
                    this._deleteNode(node, nodeName, isDestroy);
                }).start();
            } else {
                this._deleteNode(node, nodeName, isDestroy);
            }
        } else {
            console.warn(`局部缓存中没有 ${nodeName} 节点`, this._viewStack);
        }
    }

    private static _deleteNode(node, nodeName, isDestroy) {
        node.getComponent(node.name).onHide();
        if (isDestroy) {
            if (node && node.isValid) node.destroy();
            this._viewStack.delete(nodeName);
        } else {
            node.removeFromParent();
        }
    }

    /**
     * 重新加载视图，或者销毁当前视图，加载下一个视图（游戏完成，加载下一关）
     * @param deleteNodeName 想要删除的视图名称
     * @param creatMoudleName 想要创建的视图模块
     * @param createNodeName 想要创建的视图名称
     * @param position 设置位置
     * @param showAnimation 是否开启打开动画 
     */
    public static reloadMap(deleteNodeName: AssetName, creatMoudleName: string, createNodeName: AssetName, position: cc.Vec3 = new cc.Vec3(0, 0, 0), showAnimation: boolean = false) {
        if (!this._viewStack.has(deleteNodeName)) return console.error(`局部缓存中没有 ${deleteNodeName} 节点`, this._viewStack)
        let parentNode = this._viewStack.get(deleteNodeName).parent;
        this.deleteNode(deleteNodeName);
        for (var key in this._viewStack) {
            this.deleteNode(key as AssetName);
        }
        this.deleteAll();

        //下一帧再创建，等待上个节点完全销毁
        setTimeout(() => {
            this.createNode(creatMoudleName, createNodeName, { "position": position, "showAnimation": showAnimation });
        }, 160);
    }

    /**
     * 切换场景
     * @param nextSceneName 切换的场景名称
     */
    public static replaceScene(nextSceneName: string) {
        let lastSceneNode = cc.find('Canvas');
        let lastSceneName = cc.director.getScene().name;
        lastSceneNode.getComponent(lastSceneName).onHide();
        this.deleteAll();
        cc.director.loadScene(nextSceneName, () => {
            let nextSceneNode = cc.find('Canvas');
            nextSceneNode.getComponent(nextSceneName).onShow();
        });
    }

    /**
     * 清楚全部UI栈
     */
    public static deleteAll() {
        this._viewStack.forEach((value, key, map) => {
            // console.log(value, key);
            if (value.isValid) {
                this.deleteNode(key as AssetName);
            }
        });
    }

}